//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Eas.WebCrawler.Interfaces;
using Eas.WebCrawler.Model;
using Eas.WebCrawler.Model.StateTrackers;
using Eas.WebCrawler.Model.Core;
using Eas.WebCrawler.Model.Actions;

namespace Eas.WebCrawler.Test.Model {
    /// <summary>
    /// Summary description for BaseStateTrackerTest
    /// </summary>
    [TestClass]
    public class BaseStateTrackerTest {
        public BaseStateTrackerTest() {
            //
            // TODO: Add constructor logic here
            //
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 

        bool _WasStopped = false;
        bool _WasPaused = false;

        [TestInitialize()]
        public void MyTestInitialize() {
            _WasPaused = false;
            _WasStopped = false;
        }

        //
        // Use TestCleanup to run code after each test has run
        [TestCleanup()]
        public void MyTestCleanup() {
            _WasPaused = false;
            _WasStopped = false;

            GC.Collect();
        }

        //
        #endregion


        [TestMethod]
        /// <summary>Executes the SimpleTest test.</summary>
        public void SimpleTest() {
            using (WorkerContext context = CreateContext(1)) {

                Assert.AreEqual<int>(1, context.StateTracker.GetCountOfWorkersInAction(ActionId.Unstarted));
            }
            using (WorkerContext context = CreateContext(1)) {

                Assert.AreEqual<int>(1, context.StateTracker.GetCountOfWorkersInAction(ActionId.Unstarted));
                Transition(1, ActionId.Unstarted, ActionId.GetRequest, context);
                Assert.AreEqual<int>(0, context.StateTracker.GetCountOfWorkersInAction(ActionId.Unstarted));
            }
        }


        [TestMethod]
        /// <summary>Executes the TestSetGetRequest test.</summary>
        public void TestSetGetRequest() {
            using (WorkerContext context = CreateContext(1)) {
                using (TestProcessingState state = new TestProcessingState(0)) {
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                    context.StateTracker.RequestChange(WorkerState.Working);
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                    context.StateTracker.LoadChangeRequestsIntoState(state);
                    WorkerState? change = state._ChangeQueue.DeQueueRequest();
                    Assert.IsTrue(change.HasValue);
                    Assert.AreEqual<WorkerState>(WorkerState.Working, change.Value);
                    context.StateTracker.LoadChangeRequestsIntoState(state);
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                }
            }
        }

        [TestMethod]
        /// <summary>Executes the TestSetGetMultipleChangeRequests test.</summary>
        public void TestSetGetMultipleChangeRequests() {
            using (WorkerContext context = CreateContext(1)) {
                using (TestProcessingState state = new TestProcessingState(0)) {
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                    context.StateTracker.RequestChange(WorkerState.Working);
                    context.StateTracker.RequestChange(WorkerState.Paused);
                    context.StateTracker.RequestChange(WorkerState.Working);
                    context.StateTracker.RequestChange(WorkerState.Stopped);
                    context.StateTracker.LoadChangeRequestsIntoState(state);
                    Assert.AreEqual<WorkerState>(WorkerState.Working, state._ChangeQueue.DeQueueRequest().Value);
                    Assert.AreEqual<WorkerState>(WorkerState.Paused, state._ChangeQueue.DeQueueRequest().Value);
                    Assert.AreEqual<WorkerState>(WorkerState.Working, state._ChangeQueue.DeQueueRequest().Value);
                    Assert.AreEqual<WorkerState>(WorkerState.Stopped, state._ChangeQueue.DeQueueRequest().Value);
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                    Assert.IsFalse(state._ChangeQueue.DeQueueRequest().HasValue);
                }
            }
        }

        [TestMethod]
        /// <summary>Executes the TestDrainToStop test.</summary>
        public void TestDrainToStop() {
            using (WorkerContext context = CreateContext(3)) {
                Assert.IsTrue(context.StateTracker.GetCountOfWorkersInAction(ActionId.Unstarted) == 3);
                for (int i = 0; i < 3; ++i) {
                    Assert.AreEqual<WorkerState>(WorkerState.Unstarted, context.StateTracker[i].WorkerState);
                }

                Transition(2, ActionId.Unstarted, ActionId.Terminal, context);
                Transition(1, ActionId.Unstarted, ActionId.Terminal, context);
                Assert.IsFalse(_WasPaused);
                Assert.IsFalse(_WasStopped);
                Transition(0, ActionId.Unstarted, ActionId.Terminal, context);
                Assert.IsFalse(_WasPaused);
                Assert.IsTrue(_WasStopped);
            }
        }

        [TestMethod]
        /// <summary>Executes the TestDrainToPause test.</summary>
        public void TestDrainToPause() {
            using (WorkerContext context = CreateContext(3)) {
                Assert.AreEqual<int>(3, context.StateTracker.GetCountOfWorkersInAction(ActionId.Unstarted));
                Transition(2, ActionId.Unstarted, ActionId.Paused, context);
                Transition(1, ActionId.Unstarted, ActionId.Paused, context);
                Assert.IsFalse(_WasPaused);
                Assert.IsFalse(_WasStopped);
                Transition(0, ActionId.Unstarted, ActionId.Paused, context);
                Assert.IsTrue(_WasPaused);
                Assert.IsFalse(_WasStopped);
            }
        }


        internal void Transition(int id, ActionId oldActionId, ActionId newActionId, WorkerContext context) {
            using (TestProcessingState newProcessingState = new TestProcessingState(id, true, false, ActionHelper.TranslatActionIDToWorkerState(newActionId))) {
                TestAction oldAction = new TestAction(oldActionId);
                TestAction newAction = new TestAction(newActionId);
                context.StateTracker.ReportStateChange(newProcessingState, oldAction, newAction);
            }
        }

        internal WorkerContext CreateContext(int workerCount) {

            TestSettings settings = new TestSettings();
            settings.WorkerCount = workerCount;
            Eas.WebCrawler.Model.Core.WorkerContext context = Eas.WebCrawler.Model.WebCrawlerFactory.CreateWorkerContext(settings, new Eas.WebCrawler.Model.Gateway.BaseWebGateway(), new Eas.WebCrawler.Model.Actions.BaseActionFactory());
            context.Events.Paused += new EventHandler(Events_AfterPause);
            context.Events.Stopped += new EventHandler(Events_AfterStop);
            return context;
        }

        void Events_AfterStop(object sender, EventArgs e) {
            _WasStopped = true;
        }

        void Events_AfterPause(object sender, EventArgs e) {
            _WasPaused = true;
        }

    }
}
